#include "Seqlist.h"

// 初始化

void SLInit (SL* ps) // 涉及到了实参的改变的函数,一定要传址
{

    ps -> arr = NULL ;

    ps -> capacity = ps -> size = 0 ;

}

// 删除

void SLDestory (SL* ps)
{

    assert (ps) ;

    if (ps -> arr) // 判断是否为空,非空就释放
        free (ps -> arr) ;

    ps -> capacity = ps -> size = 0 ;

}

// 封装一个函数,检查顺序表空间,如果没空间就创建空间

void SLCheckCapacity (SL* ps)
{

    assert (ps) ;

    // 判断空间是否足够

    if (ps -> size == ps -> capacity)
    {

        // 增容
        // realloc的第二个参数单位是字节

        int newCapacity = ps -> capacity == 0 ? 4 : 2 * ps -> capacity ; // 这里的增容涉及两个原则: 当没有没有容量的时候,初始四个元素、当原有容量用满的时候,就扩大为原来用量的二倍(概率学)

        // 所以这里的创建使用了三目操作符

        SLDatatype* tmp = (SLDatatype*) realloc (ps -> arr , sizeof (SLDatatype) * newCapacity) ;

        // 判断是否创建成功

        if (tmp == NULL)
        {

            perror ("realloc fail!") ;

            exit (1) ;

        }

        // 创建成功

        ps -> capacity = newCapacity ;

        ps -> arr = tmp ;

    }

}

// 尾插

void SLPushBack (SL* ps , SLDatatype x)
{

    assert (ps) ; // 判断指针是否为空指针

    SLCheckCapacity (ps) ;

    // 空间足够的情况下,就会直接存进内容

    ps -> arr [ps -> size++] = x ; // size不仅作为下标,还作为标记顺序表使用了多少元素,所以使用自增

}

// 头插

void SLPushFront (SL* ps , SLDatatype x)
{

    // 温柔的处理方式

    // if (ps == NULL)
    // {

    //     return 1 ;

    // }

    assert (ps) ;

    SLCheckCapacity (ps) ;

    for (int i = ps -> size ; i >= 0 ; i--)
    {

        ps -> arr [i] = ps -> arr [i - 1] ; // 顺序表中所有数据向后移一位,给前面腾位置

    }

    ps -> arr [0] = x ;

    ++ps -> size ; // 元素增加

}

// 尾删

void SLPopBack (SL* ps)
{

    // ps: 限制参数不能直接给NULL
    // ps -> size 顺序表数据不能为空

    assert (ps && ps -> size) ; // 两个有一个为假,直接断言

    --ps -> size ; // 这里尾删不需要删除数据,只需要修改下标等下次插入新数据覆盖即可

}

// 头删

void SLPopFront (SL* ps)
{

    assert (ps && ps -> size) ; // 这里需要和尾删一样

    // 头删和尾删不同,就如尾差和头插一样
    // 头删可以让之后的数据往前移,将第一个数据覆盖即可

    for (int i = 0 ; i < ps -> size - 1 ; i++) // 需要注意,这里的移动数据在最后可能循环会出现越界访问的情况,所以要设置好循环的条件
    {

        ps -> arr [i] = ps -> arr [i + 1] ; // 当循环到最后,避免出现越界访问的情况

    }

    --ps -> size ;

}

// 指定位置之前插入数据

void SLInsert (SL* ps , int pos , SLDatatype x)
{

    assert (ps) ;

    assert (pos >= 0 && pos <= ps -> size) ; // 这里的断言是为了防止插入的位置有问题

    SLCheckCapacity (ps) ;

    // 这里pos及之后的数据整体向后移动移动一位

    for (int i = ps -> size ; i > pos ; i--)
    {

        ps -> arr [i] = ps -> arr [i - 1] ;

    }

    ps -> arr [pos] = x ;

    ++ps -> size ;

}

// 删除指定位置的数据

void SLErase (SL* ps , int pos)
{

    assert (ps) ;

    assert (pos >= 0 && pos < ps -> size) ;

    // pos之后的数据整体向前移动一位,覆盖pos的数据

    for (int i = pos ; i < ps -> size - 1 ; i++)
    {

        ps -> arr [i] = ps -> arr [i + 1] ;

    }

    // 这里也很好地包容了指定位置在头部和尾部的情况,当在头部的时候可以循环删除,在尾部的时候循环不运行直接减size即可

    --ps -> size ;

}

// 打印

void SLPrint (SL* ps)
{

    assert (ps) ;

    // 数组元素的循环打印

    for (int i = 0 ; i < ps -> size ; i++)
    {

        printf ("%d " , ps -> arr [i]) ;

    }

    printf ("\n") ;
    
}

// 查找,返回值是整型(下标)

int SLFind (SL* ps , SLDatatype x)
{

    assert (ps) ;

    for (int i = 0 ; i < ps -> size ; i++)
    {

        if (ps -> arr [i] == x)
        {

            return i ; // 返回下标

        }

    }

    // 未找到

    return -1 ;

}